// 线程的封装  --- 面向对象封装一个线程

// 防止头文件被重复包含
//  1 . #pragma once
//  2 .  使用 #ifndef、#define 和 #endif 包裹代码
#ifndef _THREAD_HPP_
#define _THREAD_HPP_

#include <iostream>
#include <pthread.h>
#include <string>
#include <cstring>
#include <functional>
#include "common.hpp"
// 加入日志
#include "Log.hpp"

namespace ThreadModel
{
    // 展开日志的命名空间
    using namespace LogModel;

    static uint32_t num = 1; // 静态无符号的全局计数器

    // 线程可能处理不同的任务 , 所以用包装器包装一下函数指针
    using func_t = std::function<void()>;

    // 实现模版 , 外部可以让线程做任何事情
    class Thread
    {
    private:
        static void *Routinue(void *args) // args 要得到该线程的相关信息 , 外部传了 this , this 可以找到一个线程类型
        {
            Thread *Self = static_cast<Thread *>(args); // args 就是 this 传过来的
            Self->IsRunning();

            // 这个函数是给线程设置一个名字 , 因为默认使用被创建的名ID , 我们自己设置
            pthread_setname_np(Self->_tid, Self->_name.c_str());
            // 执行任务
            Self->_task(); // 回调函数 , 真正的调用上层用户的任务 

            return nullptr;
        }

        void IsRunning()
        {
            _isrunning = true;
        }

        void IsDetaching()
        {
            LOG(LogLevel::DEBUG) << " [ " << _name << " ] is detaching ! ";
            _isdetaching = true; // 已经被分离
        }

    public:
        Thread(func_t task)
            : _task(task)
        {
            // 每次调用构造 ++ num
            _name = "thread-" + std::to_string(num++);
        }
        ~Thread() {}

        // 启动线程
        void Start()
        {
            if (_isrunning)
                return; // 已经运行了就退出
            // 创建线程
            int n = pthread_create(&_tid, nullptr, Routinue, this);
            if (n == 0)
            {
                //LOG(LogLevel::DEBUG) << " [ "<< _name << " ] create success ! ";
            }
            else
            {
                LOG(LogLevel::ERROR) << _name << " create fail ! ";
                ERRORINFOM("pthread_create");
            }
        }

        // 线程分离 -- 可以是在创建线程之前分离
        void Detach()
        {
            if (_isdetaching)
                return; // 已经被分离了 , 就退出
            if (_isrunning)
                pthread_detach(_tid); // 如果线程运行了 , 需要被分离就用函数

            IsDetaching(); // 线程还没有运行 ,需要先分离线程 , 就把线程分离置为真 , 方便后续判断
        }

        // 停止线程
        void Stop()
        {
            // 线程运行了再终止 , 否则啥也不干 !
            if (_isrunning)
            {
                // 终止就是取消这个线程
                int n = pthread_cancel(_tid);
                if (n == 0)
                {
                    _isrunning = false; // 不运行了 !
                    //LOG(LogLevel::DEBUG) << "Stop [ " << _name << " ] success !";
                }
                else
                {
                    LOG(LogLevel::FATAL) << "Stop [ " << _name << " ] fail !";
                }
            }
        }

        // 等待线程
        void Join()
        {
            // 这里要注意 , 如果一个线程被分离了 , 就不能等待了 !
            if (_isdetaching)
            {
                LOG(LogLevel::ERROR) << _name << " is detaching , not Join !";
                return;
            }

            int n = pthread_join(_tid, &_ret);
            if (n == 0)
            {
                LOG(LogLevel::DEBUG) << "Join [ " << _name << " ] success !";
            }
            else
            {
                LOG(LogLevel::ERROR) << "Join [ " << _name << " ] fail !";
            }
        }

        //给外部提供一个获取线程名的 Get函数
        std::string GetThreadName()
        {
            return _name;
        }

    private:
        pthread_t _tid = 0;        // 线程的ID , 给缺省值 , 默认是 0  , 创建由系统分配
        std::string _name;         // 线程的名字 : 如 , thread-1
        void *_ret;                // 线程返回值
        bool _isrunning = false;   // 线程是否运行 , 默认 -- > 否
        bool _isdetaching = false; // 线程是否被分离 , 默认 -- > 否
        func_t _task;
    };

}

#endif